﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using BehaviorModel.Block.ServiceChannel;
using ComputationalCore.Helper;
using Model;
using Model.Block;
using log4net;

namespace BehaviorModel.Block.Splitter
{
    public class Splitter : ISplitter
    {
        #region Fields

        private static readonly ILog logger = LogManager.GetLogger(typeof (Splitter));

        private List<IReceiverBlock> _receiverBlocks;

        #endregion

        #region Properties

        #region IBlock

        public Guid Id { get; private set; }

        public BlockType Type
        {
            get { return BlockType.Splitter; }
        }

        #endregion

        #region ISplitter

        public SplitDiscipline Discipline { get; set; }

        #endregion

        public SplitterStorage Storage { get; private set; }

        #endregion

        #region Constructor

        public Splitter()
        {
            Id = Guid.NewGuid();
            Storage = new SplitterStorage();

            _receiverBlocks = new List<IReceiverBlock>();
        }

        #endregion

        #region Methods

        #region IBlock

        public void Initialize()
        {
        }

        #endregion

        #region ISenderBlock

        public void ConnectReceiverBlock(IReceiverBlock block)
        {
            logger.Info("Splitter is Adding Recieving Block" + block.GetType().ToString() + " with id: " + block.Id);
            _receiverBlocks.Add(block);
        }

        public void DisconnectReceiverBlock(IReceiverBlock receiverBlock)
        {
            _receiverBlocks.Remove(receiverBlock);
        }

        public void Send(Request request)
        {
            switch (Discipline)
            {
                case SplitDiscipline.MinQueue:
                    GetMinQueueBlock().Receive(request);
                    break;
                case SplitDiscipline.EqualProbability:
                    GetEqualProbabilityBlock().Receive(request);
                    break;
            }
            Storage.CountOfPassed++;
        }

        #endregion

        #region IReceiverBlock

        public void Receive(Request request)
        {
            Send(request);
        }

        #endregion

        private IReceiverBlock GetMinQueueBlock()
        {
            var scBlocks = new List<IServiceChannel>();
            scBlocks.AddRange(_receiverBlocks.Select(block => (IServiceChannel)block).Where(block => block is IServiceChannel));
            scBlocks.Sort((a, b) => a.QueueLength.CompareTo(b.QueueLength));
            foreach (var block in scBlocks.Where(block => block.MaxQueueLength == 0 || block.QueueLength < block.MaxQueueLength))
            {
                if (block.ChannelType == ServiceChannelType.Loss && block.IsBusy)
                {
                    continue;
                }
                return block;
            }

            return scBlocks.FirstOrDefault();
        }

        private IReceiverBlock GetEqualProbabilityBlock()
        {
            var rnd = RandomHelper.NextDouble();
            var count = _receiverBlocks.Count;

            return _receiverBlocks[(int) Math.Truncate(count*rnd)];
        }

        #endregion
    }
}